﻿
using System;
using System.Collections.Generic;
using System.Linq;

namespace Red.Core
{
    /// <summary>
    ///     Carries all info needed to access Depot
    /// </summary>
    public class DepotInfo
    {
        #region Types
        
        /// <summary>
        ///     Root entry points in a depot tree
        /// </summary>
        public class EntryPoint
        {
            public string Path { get; private set; }

            public bool IsReadOnly { get; private set; }

            public string DepotPrefix { get; private set; }

            public EntryPoint( string path, bool isReadOnly, string depotPrefix = "" )
            {
                Path = path;
                IsReadOnly = isReadOnly;
                DepotPrefix = depotPrefix ?? String.Empty;
            }
        }
        
        #endregion Types

        #region Properties
        
        public string RootPath { get; private set; }

        public Perforce.Mediator SourceControl { get; private set; }

        public EntryPoint[] EntryPoints { get; private set; }
        
        #endregion Properties

        #region C-tor
        
        public DepotInfo( string rootPath, Perforce.Mediator sourceControl, EntryPoint[] entryPoints )
        {
            RootPath = rootPath;
            if ( RootPath.EndsWith( ":" ) ) // Path.Combine cannot handle property the case when the first component is a drive letter without a trailing backslash
                RootPath += "\\";
            SourceControl = sourceControl;
            EntryPoints = entryPoints;
        }
        
        #endregion C-tor

        #region Public methods
        
        /// <summary>
        ///     Determines under which entry point the path resides. Searches entry points in the order they appear
        ///     on the list given at the construction time (important when entry points overlap).
        /// </summary>
        /// <param name="fullPath">
        ///     Full local path
        /// </param>
        /// <return>
        ///     Path's entry points or null if unknown
        /// </return>
        public EntryPoint GetEntryPointForPath( string fullPath )
        {
            foreach ( var entryPoint in EntryPoints )
            {
                var root = System.IO.Path.Combine( RootPath, entryPoint.Path );
                if ( fullPath.StartsWith( root ) )
                {
                    return entryPoint;
                }
            }

            return null;
        }

        /// <summary>
        ///     Determines under which entry point the path resides. Searches entry points to find the one with depot prefix 
        ///     that given path starts with. If there are multiple entry points with the same depot prefix, it returns null.
        /// </summary>
        /// <param name="depotPath">
        ///     Depot path
        /// </param>
        /// <returns>
        ///     Path's entry point or null if unknown
        /// </returns>
        public EntryPoint GetEntryPointForDepotPath( string depotPath )
        {
            List<EntryPoint> possibleEntryPoints = new List<EntryPoint>();
            foreach ( var entryPoint in EntryPoints )
            {
                if ( !string.IsNullOrEmpty( entryPoint.DepotPrefix ) && depotPath.StartsWith( entryPoint.DepotPrefix + System.IO.Path.DirectorySeparatorChar ) )
                {
                    possibleEntryPoints.Add( entryPoint );
                }
            }

            if ( possibleEntryPoints.Count == 0 )
            {
                possibleEntryPoints = EntryPoints.ToList().FindAll( ep => string.IsNullOrEmpty( ep.DepotPrefix ) );
            }

            return possibleEntryPoints.Count == 1 ? possibleEntryPoints[0] : null;
        }

        /// <summary>
        ///     Returns depot path from full local path
        /// </summary>
        /// <param name="fullPath">
        ///     Full local path
        /// </param>
        /// <return>
        ///     Path as visible in depot, or null if it's not under depot
        /// </return>
        public string GetDepotPath( string fullPath )
        {
            EntryPoint entryPoint = GetEntryPointForPath( fullPath );
            if ( entryPoint != null )
            {
                var root = System.IO.Path.Combine( RootPath, entryPoint.Path );
				// watch out, Path.Combine doesn't work with second string staring with backslashes
                var result = System.IO.Path.Combine( entryPoint.DepotPrefix, fullPath.Substring( root.Length ).TrimStart( '\\' ) );
                return result.Length > 0 && result[0] == System.IO.Path.DirectorySeparatorChar ? result.Substring( 1 ) : result;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        ///     Returns absolute path from depot path.
        ///     Result will be improper if there are multiple entry points with the same depot prefix.
        /// </summary>
        /// <param name="depotPath">
        ///     Depot path
        /// </param>
        /// <returns>
        ///     Absolute path
        /// </returns>
        public string GetAbsolutePath( string depotPath )
        {
            EntryPoint entryPoint = GetEntryPointForDepotPath( depotPath );
            if ( entryPoint != null )
            {
                var root = System.IO.Path.Combine( RootPath, entryPoint.Path );
                var result = System.IO.Path.Combine( root, depotPath.Substring( entryPoint.DepotPrefix.Length ).TrimStart( '\\' ) );
                return result;
            }
            return null;
        }
        
        #endregion Public methods
    }
}
